﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace FinsCore
{
    class Program
    {
        static Socket client = new Socket(SocketType.Stream, ProtocolType.Tcp);
        static void Main(string[] args)
        {
            TcpClient tcp = new TcpClient();

            //Ping ping = new Ping();
            //while (true)
            //{
            //    var reply = ping.Send("192.168.250.9", 1000);
            //    if (reply.Status == IPStatus.Success)
            //    {
            //        Console.WriteLine("OK");
            //        Thread.Sleep(1000);
            //    }
            //    else
            //    {
            //        Console.WriteLine("False");
            //    }
            //}

            int a = 0x01020304;
            var m = BitConverter.GetBytes(a);
            var rltCon = Conn();
            if (rltCon)
            {
                Console.Write("连接成功");
            }
            else
            {
                Console.Write("连接失败");
            }
            ReadWord("D0", 1);
           
          
            Console.WriteLine("写下要读取的地址,然后按下回车,例: D0,10");
            bool test = true;
            UInt16 testt = 0;
            while (true)
            {
                testt++;
                List<UInt16> vals = new List<ushort>(501);
                for (int i = 0; i < 200; i++)
                {
                    vals.Add(testt);
                }
                ReadWord("D0", 1);
                Console.WriteLine(ReadBit("W100.0", 2)[0]);
                //var bytemid = BitConverter.GetBytes(0.0f+testt).ToArray();
                //WriteWord("D0",new byte[]{bytemid[1],bytemid[0],bytemid[3],bytemid[2]});
                //WriteWord("D0", new byte[]{0x01,0x02});
                // WriteShort("D100", vals.ToArray() );
                //var rlts = ReadBit("C100.1", 1);
                //WriteBit("C100.1", 1, new[] { !rlts[0] });
                //test = !test;
                Thread.Sleep(1000);
                //string str= Console.ReadLine();
                //string[] addrs=new string[2];
                //if (str.Contains(','))
                //{
                //    addrs[0] = str.Split(',')[0];
                //    addrs[1] = str.Split(',')[1];
                // }
                //else
                //{
                //    addrs[0] = str.Split(',')[0];
                //    addrs[1] = "1";
                // }
                //ReadWord(addrs[0],Convert.ToUInt16(addrs[1]));
            }
        }
        static bool[] ReadBit(string addrs, ushort count)
        {
            List<byte> writeCmd = new List<byte>();
            writeCmd.AddRange(GetReadHeader());
            writeCmd.AddRange(GetFinsHeader());
            writeCmd.AddRange(GetReadCmd());
            writeCmd.AddRange(GetBodyByBit(addrs, count));
            writeCmd[7] = BitConverter.GetBytes(writeCmd.Count - 8)[0];
            writeCmd[6] = BitConverter.GetBytes(writeCmd.Count - 8)[1];
            string strs = GetHexStr(writeCmd.ToArray());
            client.Send(writeCmd.ToArray());
            var rcvBytes = new byte[1024 * 2];
            int rcvCount = client.Receive(rcvBytes);

            bool rlt = true;
            rlt &= CheckFinsTcpHeader(rcvBytes.Take(rcvCount).ToArray());
            rlt &= CheckRcvLength(rcvBytes.Take(rcvCount).ToArray());
            rlt &= CheckErrCode(rcvBytes.Take(rcvCount).ToArray());
            if (rlt)
            {
                bool[] rlts = new bool[count];
                var dataRlt = rcvBytes.Skip(30).Take(rcvCount - 30).ToArray();
                for (int i = 0; i < dataRlt.Length; i++)
                {
                    if (dataRlt[i] == 0x01)
                    {
                        rlts[i] = true;
                    }
                }
                return rlts;
            }
            return null;
        }
        static void WriteBit(string addrs, ushort count, bool[] vals)
        {
            List<byte> writeCmd = new List<byte>();
            writeCmd.AddRange(GetReadHeader());
            writeCmd.AddRange(GetFinsHeader());
            writeCmd.AddRange(GetWriteCmd());
            writeCmd.AddRange(GetBodyByBit(addrs, count));
            for (int i = 0; i < vals.Length; i++)
            {
                byte temp = vals[i] ? (byte)0x01 : (byte)0x00;
                writeCmd.Add(temp);
            }
            writeCmd[7] = BitConverter.GetBytes(writeCmd.Count - 8)[0];
            writeCmd[6] = BitConverter.GetBytes(writeCmd.Count - 8)[1];
            string readStr = GetHexStr(writeCmd.ToArray());
            client.Send(writeCmd.ToArray());
            var rcvBytes = new byte[1024 * 2];
            int rcvCount = client.Receive(rcvBytes);

            bool rlt = true;
            rlt &= CheckFinsTcpHeader(rcvBytes.Take(rcvCount).ToArray());
            rlt &= CheckRcvLength(rcvBytes.Take(rcvCount).ToArray());
            rlt &= CheckErrCode(rcvBytes.Take(rcvCount).ToArray());
            if (rlt)
            {
                var dataRlt = rcvBytes.Skip(30).Take(rcvCount - 30).ToArray();

                for (int i = 0; i < dataRlt.Length; i = i + 2)
                {
                    Console.WriteLine(BitConverter.ToInt16(new byte[] { dataRlt[i + 1], dataRlt[i] }, 0));
                }
            }
        }
        static void ReadWord(string addrs, ushort count)
        {
            List<byte> readCmd = new List<byte>();
            readCmd.AddRange(GetReadHeader());
            readCmd.AddRange(GetFinsHeader());
            readCmd.AddRange(GetReadCmd());
            readCmd.AddRange(GetBodyByInt(addrs, count));
            readCmd[7] = BitConverter.GetBytes(readCmd.Count - 8)[0];
            readCmd[6] = BitConverter.GetBytes(readCmd.Count - 8)[1];
            string readStr = GetHexStr(readCmd.ToArray());
            client.Send(readCmd.ToArray());
            var rcvBytes = new byte[1024 * 2];
            int rcvCount = client.Receive(rcvBytes);

            bool rlt = true;
            rlt &= CheckFinsTcpHeader(rcvBytes.Take(rcvCount).ToArray());
            rlt &= CheckRcvLength(rcvBytes.Take(rcvCount).ToArray());
            rlt &= CheckErrCode(rcvBytes.Take(rcvCount).ToArray());
            if (rlt)
            {
                var dataRlt = rcvBytes.Skip(30).Take(rcvCount - 30).ToArray();

                for (int i = 0; i < dataRlt.Length; i = i + 2)
                {
                    Console.WriteLine(BitConverter.ToInt16(new byte[] { dataRlt[i + 1], dataRlt[i] }, 0));
                }
            }
        }

        static void WriteWord(string addrs, byte[] bytes)
        {
            List<byte> writeCmd = new List<byte>();
            writeCmd.AddRange(GetReadHeader());
            writeCmd.AddRange(GetFinsHeader());
            writeCmd.AddRange(GetWriteCmd());
            writeCmd.AddRange(GetBodyByInt(addrs, (ushort)(bytes.Length / 2)));
            writeCmd.AddRange(bytes);
            writeCmd[7] = BitConverter.GetBytes(writeCmd.Count - 8)[0];
            writeCmd[6] = BitConverter.GetBytes(writeCmd.Count - 8)[1];
            string readStr = GetHexStr(writeCmd.ToArray());
            client.Send(writeCmd.ToArray());
            var rcvBytes = new byte[1024 * 2];
            int rcvCount = client.Receive(rcvBytes);

            bool rlt = true;
            rlt &= CheckFinsTcpHeader(rcvBytes.Take(rcvCount).ToArray());
            rlt &= CheckRcvLength(rcvBytes.Take(rcvCount).ToArray());
            rlt &= CheckErrCode(rcvBytes.Take(rcvCount).ToArray());
            if (rlt)
            {
                var dataRlt = rcvBytes.Skip(30).Take(rcvCount - 30).ToArray();

                for (int i = 0; i < dataRlt.Length; i = i + 2)
                {
                    Console.WriteLine(BitConverter.ToInt16(new byte[] { dataRlt[i + 1], dataRlt[i] }, 0));
                }
            }
        }

        static void WriteShort(string addrs, UInt16[] vals)
        {
            var bytes = new List<byte>(vals.Length * 2);
            foreach (var val in vals)
            {
                bytes.AddRange(BitConverter.GetBytes(val).Reverse());
            }
            WriteWord(addrs, bytes.ToArray());
        }

        static bool Conn()
        {
            var bytes = GetConnHeader();
            try
            {
                client.Connect(new IPEndPoint(IPAddress.Parse("192.168.250.9"), 9600));
                Console.WriteLine("TCP连接成功");
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
                return false;
            }
            var rcvBytes = new byte[1024 * 1024];
            try
            {
                client.Send(bytes);
                int count = client.Receive(rcvBytes);
                if (count != 24)
                {
                    return false;
                }
                string strs = String.Empty;
                for (int i = 0; i < 24; i++)
                {
                    strs += rcvBytes[i].ToString("X");
                }
                Console.WriteLine("接收到信息" + strs);
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }


            bool conRlt = true;



            conRlt &= BitConverter.ToInt32(GetReverse(rcvBytes, 8, 4), 0) == 1;
            //conRlt &= BitConverter.ToInt32(GetReverse(rcvBytes, 12, 4), 0) == 0;
            return conRlt;

        }
        /// <summary>
        /// 连接报文
        /// </summary>
        /// <returns></returns>
        static byte[] GetConnHeader()
        {
            var bytes = new List<byte>(20);
            bytes.AddRange(new byte[] { 0x46, 0x49, 0x4E, 0x53, });//finstcp头
            bytes.AddRange(new byte[] { 0x00, 0x00, 0x00, 0x0C, });//报文长度地址
            bytes.AddRange(new byte[] { 0x00, 0x00, 0x00, 0x00, });//command命令
            bytes.AddRange(new byte[] { 0x00, 0x00, 0x00, 0x00, });//ErrorCode
            bytes.AddRange(new byte[] { 0x00, 0x00, 0x00, 0x0a }); //Client Node Address
            return bytes.ToArray();
        }
        /// <summary>
        /// 读写操作报文头
        /// </summary>
        /// <returns></returns>
        static byte[] GetReadHeader()
        {
            List<byte> bytes = new List<byte>(20);
            bytes.AddRange(new byte[] { 0x46, 0x49, 0x4E, 0x53, });//fins头
            bytes.AddRange(new byte[] { 0x00, 0x00, 0x00, 0x00, });//长度地址
            bytes.AddRange(new byte[] { 0x00, 0x00, 0x00, 0x02, });//command信息 02表示 是fins帧数据
            bytes.AddRange(new byte[] { 0x00, 0x00, 0x00, 0x00, });//ErrorCode
            return bytes.ToArray();
        }
        /// <summary>
        /// 反转数组
        /// </summary>
        /// <param name="bytes"></param>
        /// <param name="start"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        static byte[] GetReverse(byte[] bytes, int start, int count)
        {
            return bytes.Skip(start).Take(count).Reverse().ToArray();
        }
        static byte[] GetFinsHeader()
        {
            byte[] fins = new byte[10];
            fins[0] = 0x80;  //发送数据时 开头固定80 ICF
            fins[1] = 00;   // 固定 00 RSV 
            fins[2] = 06;   //固定02 GCT 
            fins[3] = 00;   //目标网络号DNA  固定为00   不等于00 时需要配置路由表
            fins[4] = 09;   //目标节点号 DA１  目标的IP //todo 和目标PLCIP末字节锻炼
            fins[5] = 00;   //目标单元号 DA2 设置为00就行
            fins[6] = 00;   //源网络号 SNA 固定为00   不等于00 时需要配置路由表
            fins[7] = 0x0a;   //源节点号 SA1 上位机的IP末字节 
            fins[8] = 00;   //源单元号 SA2 固定设为00
            fins[9] = 00;   //SID 固定为00 
            return fins;
        }
        static byte[] GetReadCmd()
        {
            return new byte[] { 0x01, 0x01 };
        }
        static byte[] GetWriteCmd()
        {
            return new byte[] { 0x01, 0x02 };
        }
        static byte[] GetBodyByInt(string addr, ushort count)
        {
            char start = addr[0];
            byte areabyte;
            switch (start)
            {
                case 'D':
                    areabyte = 0x82;
                    break;
                case 'H':
                    areabyte = 0xB2;
                    break;
                case 'W':
                    areabyte = 0xB1;
                    break;
                default:
                    areabyte = 0x82;
                    break;
            }

            var startByte = BitConverter.GetBytes(Convert.ToInt16(addr.Remove(0, 1)));
            byte bitByte = 0x00; //位操作地址 进行字操作时  写0
            var lengthByte = BitConverter.GetBytes((ushort)(count));
            List<byte> bytes = new List<byte>(10) { areabyte };
            bytes.AddRange(startByte.Reverse());
            bytes.Add(bitByte);
            bytes.AddRange(lengthByte.Reverse());
            return bytes.ToArray();
        }
        static string GetHexStr(byte[] bytes, int count = 0)
        {
            int length = count == 0 ? count = bytes.Length : count;
            string str = String.Empty;
            for (int i = 0; i < length; i++)
            {
                str += bytes[i].ToString("X");
            }
            return str;
        }
        static bool CheckFinsTcpHeader(byte[] rcvBytes)
        {
            return Encoding.ASCII.GetString(rcvBytes, 0, 4) == "FINS";
        }
        static bool CheckRcvLength(byte[] rcvBytes)
        {
            return BitConverter.ToInt32(GetReverse(rcvBytes, 4, 4), 0) == rcvBytes.Length - 8;
        }
        static bool CheckErrCode(byte[] rcvBytes)
        {
            return BitConverter.ToInt32(GetReverse(rcvBytes, 12, 4), 0) == 0;
        }
        /// <summary>
        /// 根据地址和读取的长度   获得
        /// </summary>
        /// <param name="addrs">操作地址</param>
        /// <param name="count">操作长度</param>
        /// <returns></returns>
        static byte[] GetBodyByBit(string addrs, ushort count)
        {
            char start = addrs[0];
            byte areabyte;
            switch (start)
            {
                case 'C':
                    areabyte = 0x30;
                    break;
                case 'W':
                    areabyte = 0x31;
                    break;
                case 'H':
                    areabyte = 0x32;
                    break;
                case 'A':
                    areabyte = 0x33;
                    break;
                default:
                    areabyte = 0x82;
                    break;
            }

            var startByte = BitConverter.GetBytes(Convert.ToInt16(addrs.Remove(0, 1).Split('.')[0]));
            byte bitByte = BitConverter.GetBytes(Convert.ToInt16(addrs.Remove(0, 1).Split('.')[1]))[0]; ; //中间字节  表示位的位置
            var lengthByte = BitConverter.GetBytes(count);
            List<byte> bytes = new List<byte>(10) { areabyte };
            bytes.AddRange(startByte.Reverse());
            bytes.Add(bitByte);
            bytes.AddRange(lengthByte.Reverse());
            return bytes.ToArray();
        }


    }
}
